//
//  JSVirtualMachine.m
//  iCodeMac
//
//  Created by Andrew Barry on 4/03/09.
//  Copyright 2009 Barry Computer Services. All rights reserved.
//

#import "JSVirtualMachine.h"
#include <ucontext.h>
#import "AppModule.h"

struct fullcontext {
	ucontext_t context;
	_STRUCT_MCONTEXT  __some_context_mc;
};

static void runjs(JSVirtualMachine *vm, AppModule *module) {
	[module runScript];
	[vm finished];
}

static void jscallfunction(JSVirtualMachine *vm, JSContext *cx, JSObject *target, jsval prop) {
	jsval rv = 0;
	bool success;
	
	success = JS_CallFunctionValue(cx, target, prop, 0, nil, &rv);
	[vm finished];
}

@implementation JSVirtualMachine
-(id)initWithStack:(AppStack *) s {
	self = [self init];
	stack = [s retain];
	return self;
}

-(bool)isRunningVM {
	return inVM;
}

-(bool)isSuspended {
	return suspendedVM;
}

-(void)runModule: (AppModule *) module {
	ucontext_t *context;
	if (!vmContext) {
		context = malloc(sizeof(struct fullcontext));
		mainContext = malloc(sizeof(struct fullcontext));
		vmContext = context;
		contextStack = malloc(65536);
		
		memset(context, 0, sizeof(struct fullcontext));
	} else {
		context = vmContext;
	}
	getcontext(context);
	context->uc_stack.ss_sp = contextStack;
	context->uc_stack.ss_size = 65536;
	makecontext(context, runjs, 2, self, module);
	inVM = true;
	[stack updateTitle];
	swapcontext(mainContext, context);
}

-(void)callFunction: (JSContext *) cx withTarget: (JSObject *) target withFunction: (jsval)  prop {
	ucontext_t *context = vmContext;
	getcontext(context);
	context->uc_stack.ss_sp = contextStack;
	context->uc_stack.ss_size = 65536;
	makecontext(vmContext, jscallfunction, 4, self, cx, target, prop);
	inVM = true;
	[stack updateTitle];
	swapcontext(mainContext, vmContext);
}

-(void)finished {
	if (inVM) {
		inVM = false;
		[stack updateTitle];
		swapcontext(vmContext, mainContext);
	}
}

-(void)suspend {
	if (inVM) {
		inVM = false;
		suspendedVM = true;
		[stack updateTitle];
		swapcontext(vmContext, mainContext);
	}
}

-(void)resume {
	if (!inVM && suspendedVM) {
		inVM = true;
		suspendedVM = false;
		[stack updateTitle];
		swapcontext(mainContext, vmContext);
	}
}

-(void)stop {
	if (!inVM && suspendedVM) {
		stop = true;
		inVM = true;
		suspendedVM = false;
		[stack updateTitle];
		swapcontext(mainContext, vmContext);
	}
}
@end
